package chabernac.gravidroid.model;

import java.util.List;

import chabernac.gravidroid.model.Structure.Intersection;

public class ForceCalculator {
	// private static final float MAX_FORCE = 1000000;
	private static final float MAX_FORCE = 10000;
	public static final float G = 1000;
	// public static final float G = 1;
	public static final float R = 1000;

	private static Vector myForceVector = new Vector();

	public static iMass calculateForce(Mass aMass, LinearMass aLinearMass) {
		Rectangle theBoxingRect = aLinearMass.getBoxingRect();
		Rectangle theOuterBoxingRect = theBoxingRect.addBorder(aMass.coreRadius);
		if (theOuterBoxingRect.contains(aMass.location)) {
			// the mass is within the boxing rect of the linear mass, there might be a
			// collision
			Line theLine = aLinearMass.getLine();
			// first calculate the distance to the line

			float theDistance = Math.abs(theLine.distanceToPoint(aMass.location));

			if (theDistance < aMass.coreRadius) {
				// Log.d( ForceCalculator.class.getName(), "Collision detected!!!" );
				// only if the distance from the mass to the line is smaller then the
				// radius of the mass there is a collision
				// between
				// the mass and the linearmass
				// now calculate the intersection point
				Line theOrhtogonal = theLine.getOrthogonalLineThroughPoint(aMass.location);
				Vector theIntersection = theOrhtogonal.intersect(theLine);

				if (theBoxingRect.contains(theIntersection)) {
					calculateForceOnCollision(aMass, aLinearMass, theDistance, theIntersection, null);
					return aLinearMass;
				} else {
					if (aMass.isCollisionWith(aLinearMass.getMass1())) {
						calculateForce(aMass, aLinearMass.getMass1());
						return aLinearMass.getMass1();
					}

					if (aMass.isCollisionWith(aLinearMass.getMass2())) {
						calculateForce(aMass, aLinearMass.getMass2());
						return aLinearMass.getMass2();
					}
					// the collision is only with one of the two endpoint of the line
				}
			}
		}

		return null;

		// if the mass does not collide with line we just calculate the forces
		// between the masses like otherwise
		// calculateForce(aMass, aLinearMass.getMass1());
		// calculateForce(aMass, aLinearMass.getMass2());

	}

	private static void calculateForceOnCollision(Mass aMass, LinearMass aLinearMass, float aDistance, Vector aIntersection,
			Vector anOrthogonal) {
		// this is not logical, it should be some constant, impuls law?
		float theGMultipMsass = R * (aMass.mass + aLinearMass.getMass1().mass + aLinearMass.getMass2().mass);
		// now we have to the point on the line at which the mass collides with the
		// linear mass.
		float theForce = limitForce(theGMultipMsass / (float) Math.pow(aDistance / 200, 3));
		System.out.println("Force " + theForce + " at distance " + aDistance);

		// now we must split the force to act one the two masses of the linearmass

		float theLinearMassLength = new Vector(aLinearMass.getMass1().location, aLinearMass.getMass2().location).getLength();

		float theLengthFromIntersectionToP1 = new Vector(aIntersection, aLinearMass.getMass1().location).getLength();
		float theLengthFromIntersectionToP2 = theLinearMassLength - theLengthFromIntersectionToP1;

		float theForceOnMass1 = theForce * theLengthFromIntersectionToP2 / theLinearMassLength;
		float theForceOnMass2 = theForce * theLengthFromIntersectionToP1 / theLinearMassLength;

		// this force vector points from the mass to the line, so its the force that
		// must be applied to the line
		Vector theForceVectorOnMass1 = null;
		if (anOrthogonal != null) {
			theForceVectorOnMass1 = anOrthogonal;
		} else {
			theForceVectorOnMass1 = new Vector(aMass.location, aIntersection);
		}
		theForceVectorOnMass1.normalize(theForceVectorOnMass1);

		Vector theForceVectorOnMass2 = new Vector(theForceVectorOnMass1.x, theForceVectorOnMass1.y);
		Vector theForceVectorOnMass = new Vector(theForceVectorOnMass1.x, theForceVectorOnMass1.y);

		// the force on mass1 of the linearmass
		theForceVectorOnMass1.multiply(theForceOnMass1, theForceVectorOnMass1);

		// the forec on mass2 of the linearmass
		theForceVectorOnMass2.multiply(theForceOnMass2, theForceVectorOnMass2);

		// the force on given mass
		theForceVectorOnMass.multiply(-theForce, theForceVectorOnMass);

		aLinearMass.getMass1().force.add(theForceVectorOnMass1, aLinearMass.getMass1().force);
		aLinearMass.getMass2().force.add(theForceVectorOnMass2, aLinearMass.getMass2().force);
		aMass.force.add(theForceVectorOnMass, aMass.force);
		// System.out.println("Resulting force on mass " + theForceVectorOnMass);
	}

	private static float limitForce(float aForce) {
		if (aForce < -MAX_FORCE) {
			return -MAX_FORCE;
		}
		if (aForce > MAX_FORCE) {
			return MAX_FORCE;
		}
		return aForce;
	}

	public static void calculateForce(Mass aMass, Mass anOtherMass) {
		if (aMass == anOtherMass)
			return;
		float theTotalRadius = aMass.radius + anOtherMass.radius;
		myForceVector.set(anOtherMass.location.x - aMass.location.x, anOtherMass.location.y - aMass.location.y);

		float theForceLength = myForceVector.getLength();
		// float theLengthMinusByRadius = theForceLength / theTotalRadius;
		float theLengthDevidedByFactor = theForceLength / 200;
		float theGMultipMsass = G * anOtherMass.mass * aMass.mass;
		// float theAttraction = theGMultipMsass /
		// (float)Math.pow(theLengthMinusByRadius, 2);
		// float theRepulsion = 0;

		// boolean isCollision = theForceLength < theTotalRadius;

		float theForce = 0;

		if (theForceLength < theTotalRadius && theGMultipMsass > 0) {
			// collision --> repulsion
			theForce = -theGMultipMsass / (float) Math.pow(theLengthDevidedByFactor, 4);
			aMass.addCollidingMass(anOtherMass);
			anOtherMass.addCollidingMass(aMass);
		} else {
			if (!aMass.isAttractsOtherMasses)
				return;
			if (!anOtherMass.isAttractsOtherMasses)
				return;

			theForce = theGMultipMsass / (float) Math.pow(theLengthDevidedByFactor, 2);
		}

		// if(theAttraction > 0){
		// theRepulsion = theGMultipMsass / (float)Math.pow(theLengthMinusByRadius,
		// 2.5);
		// }
		// float theForce = theAttraction - theRepulsion;

		if (theForce < -MAX_FORCE) {
			theForce = -MAX_FORCE;
		}
		if (theForce > MAX_FORCE) {
			theForce = MAX_FORCE;
		}
		myForceVector.multiply(theForce / theForceLength, myForceVector);

		aMass.force.add(myForceVector, aMass.force);
		myForceVector.multiply(-1, myForceVector);
		anOtherMass.force.add(myForceVector, anOtherMass.force);
	}

	public static void calculateForce(LinearMass aMass, LinearMass anOtherMass) {
		calculateForce(aMass.getMass1(), anOtherMass.getMass1());
		calculateForce(aMass.getMass1(), anOtherMass.getMass2());
		calculateForce(aMass.getMass2(), anOtherMass.getMass1());
		calculateForce(aMass.getMass2(), anOtherMass.getMass2());
	}

	public static void calculateForce(Mass aMass, LineForce lineForce) {
		Line theLine = lineForce.getLine();
		float theDistannce = theLine.distanceToPoint(aMass.location) / 50;
		// System.out.println("Distance: " + theDistannce);
		float theForce = 1000 * G / (theDistannce * theDistannce * theDistannce * theDistannce);
		// System.out.println("Force: " + theForce);
		if (theForce > 100000 || theDistannce < 0) {
			theForce = 100000;
		}
		myForceVector.set(theLine.A, theLine.B);
		myForceVector.normalize(myForceVector);
		myForceVector.multiply(theForce, myForceVector);

		aMass.force.add(myForceVector, aMass.force);
	}

	public static void calculateForce(LineForce lineForce, LineForce aMass) {
	}

	public static void calculateForce(LineForce aMass, LinearMass aLinearMass) {
		calculateForce(aLinearMass.getMass1(), aMass);
		calculateForce(aLinearMass.getMass2(), aMass);
	}

	public static void calculateForce(Mass aMass, Structure aStructure) {
		for (Mass theMass : aStructure.getInteralMasses()) {
			calculateForce(aMass, theMass);
		}
		// iMass theCollidedMass = null;
		// for (LinearMass theLinearMass : aStructure.getLinearMasses()) {
		// if (!linearMassContains(theLinearMass, theCollidedMass)) {
		// theCollidedMass = calculateForce(aMass, theLinearMass);
		// }
		// }

	}

	private static boolean linearMassContains(LinearMass aLinearMass, iMass aMass) {
		if (aMass == null)
			return false;
		if (aLinearMass == aMass)
			return true;
		if (aLinearMass.getMass1() == aMass)
			return true;
		if (aLinearMass.getMass2() == aMass)
			return true;
		return false;
	}

	public static void calculateForce(LinearMass aMass, Structure aStructure) {
		Intersection theIntersection = aStructure.getIntersection(aMass);
		if (theIntersection != null) {
			if (theIntersection.linearMass != null) {
				theIntersection.linearMass.calculateIntrusiveForce(theIntersection.mass);
			}
		}
	}

	public static void calculateForce(LineForce aMass, Structure aStructure) {
		for (LinearMass theLinearMass : aStructure.getLinearMasses()) {
			calculateForce(aMass, theLinearMass);
		}
	}

	public static void calculateForce(Structure aStructure1, Structure aStructure2) {
		List<Intersection> theIntersections = aStructure1.getIntersection(aStructure2);
		for (Intersection theIntersection : theIntersections) {
			if (theIntersection.linearMass != null) {
				theIntersection.linearMass.calculateIntrusiveForce(theIntersection.mass);
			}
		}
	}
}
